<?php


namespace app\admin\logic;


use think\contract\Arrayable;
use think\facade\Db;
use think\facade\View;

/**
 * Created by PhpStorm
 * USER zhangkai QQ 920062039
 * Date 2022/6/16   16:30
 */
class CodeGeneration extends LogicBase
{
    private $ignoreTable = ['role', 'user', 'user_role', 'migrations'];

    /**
     * 获取数据库表
     */
    public function getTableList()
    {
        $database = config('database.connections')[config('database.default')];

        $tableInfo = Db::query(
            'select table_name,table_comment from information_schema.tables where table_schema=:database',
            ['database' => $database['database']]
        );

        $tableInfo = array_filter($tableInfo, function ($item) use ($database) {
            return !in_array(str_replace($database['prefix'], '', $item['table_name']), $this->ignoreTable);
        });

        // 过滤系统系统内置的数据库表
        $tableInfo = array_slice($tableInfo, 0);
        return array_map(function ($item) {
            return [
                'label' => $item['table_comment'],
                'value' => $item['table_name']
            ];
        }, $tableInfo);
    }

    /**
     * 获取数据库表字段信息
     */
    public function getTableColumns($data)
    {
        $database = config('database.connections')[config('database.default')];
        $table = $data['table'];
        $sql = 'select column_name,column_comment,data_type,is_nullable,character_maximum_length from information_schema.columns where table_name=:tablename and table_schema=:database';
        $tableStruct = Db::query($sql, ['tablename' => $table, 'database' => $database['database']]);

        $ignoreColumns = ['id', 'uuid', 'creator', 'updater', 'delete_time'];

        $result = array_filter($tableStruct, function ($item) use ($ignoreColumns) {
            return !in_array($item['column_name'], $ignoreColumns);
        });

        $result = array_slice($result, 0);

        return $result;
    }

    /**
     * 代码生成
     */
    public function generateCode($data)
    {

        validate(\app\common\validate\CodeGeneration::class)->scene('generation')->check($data);

        // 表名无前缀
        $database = config('database.connections')[config('database.default')];
        $data['table'] = str_replace($database['prefix'], '', $data['table']);
        $data['model'] = $data['table'];

        // 生成vue代码
        $this->generateVueCode($data);
        // 生成php代码
        $this->generatePhpCode($data);

    }

    /**
     * 生成vue代码
     */
    private function generateVueCode($data)
    {

        $vueRootPath = $data['vuePath'];
        $isRewrite = empty($data['isRewrite']) ? false : $data['isRewrite'];

        $vueIndex = $this->generateVueIndex($data);
        $vueEdit = $this->generateVueEdit($data);
        $vueDetail = $this->generateVueDetail($data);
        $vueApi = $this->generateVueApi($data);
        $vueRoute = $this->generateVueRoute($data);
        $vueIndexPath = $vueRootPath . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $data['table'] . DIRECTORY_SEPARATOR . 'index.vue';
        $vueEditPath = $vueRootPath . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $data['table'] . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'Edit.vue';
        $vueDetailPath = $vueRootPath . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $data['table'] . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'Detail.vue';
        $vueApiPath = $vueRootPath . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . $data['table'] . '.js';
        $vueRoutePath = $vueRootPath . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'router' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $data['table'] . '.js';

        $this->writeFile($vueEditPath, $vueEdit, $isRewrite);
        $this->writeFile($vueDetailPath, $vueDetail, $isRewrite);
        $this->writeFile($vueIndexPath, $vueIndex, $isRewrite);
        $this->writeFile($vueApiPath, $vueApi, $isRewrite);
        $this->writeFile($vueRoutePath, $vueRoute, $isRewrite);
    }

    /**
     * 生成php代码
     */
    private function generatePhpCode($data)
    {
        $data['fmodel'] = $this->tableToClassName($data['model']);
        $isRewrite = empty($data['isRewrite']) ? false : $data['isRewrite'];

        $phpController = $this->generatePhpController($data);
        $phpLogic = $this->generatePhpLogic($data);
        $phpModel = $this->generatePhpModel($data);
        $phpValidate = $this->generatePhpValidate($data);

        $controllerPath = app()->getBasePath() . DIRECTORY_SEPARATOR . $data['module'] . DIRECTORY_SEPARATOR . 'controller' . DIRECTORY_SEPARATOR . $data['fmodel'] . '.php';
        $logicPath = app()->getBasePath() . DIRECTORY_SEPARATOR . $data['module'] . DIRECTORY_SEPARATOR . 'logic' . DIRECTORY_SEPARATOR . $data['fmodel'] . '.php';
        $modelPath = app()->getBasePath() . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR . $data['fmodel'] . '.php';
        $validatePath = app()->getBasePath() . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'validate' . DIRECTORY_SEPARATOR . $data['fmodel'] . '.php';

        $this->writeFile($controllerPath, $phpController, $isRewrite);
        $this->writeFile($logicPath, $phpLogic, $isRewrite);
        $this->writeFile($modelPath, $phpModel, $isRewrite);
        $this->writeFile($validatePath, $phpValidate, $isRewrite);

        // 自动增加route 路由信息
        $this->autoAddRoute($data);

        // 自动增加权限配置信息
        $this->autoAddAuth($data);
    }

    /**
     * 生成php Controller
     */
    private function generatePhpController($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module'], 'comment'=>$data['comment']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'controller' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;

    }

    /**
     * 生成php Logic
     */
    private function generatePhpLogic($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'logic' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 生成php Model
     */
    private function generatePhpModel($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 生成php Validate
     */
    private function generatePhpValidate($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'validate' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 生成php route
     */
    private function generatePhpRoute($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 生成php auth
     */
    private function generatePhpAuth($data)
    {
        $fetchData = ['model' => $data['model'], 'fmodel' => $data['fmodel'], 'module' => $data['module'], 'comment' => $data['comment']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'back' . DIRECTORY_SEPARATOR . 'auth' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 自动增加php route信息
     */
    private function autoAddRoute($data)
    {
        // 获取不包含当前表的干净的路由
        $cleanRoute = $this->getNoCurrentTableRoute($data);
        $temp = $this->generatePhpRoute($data);

        $routeResult = $cleanRoute . PHP_EOL . $temp;
        $routePath = app()->getAppPath() . 'route' . DIRECTORY_SEPARATOR . 'app.php';

        $this->writeFile($routePath, $routeResult, true);
    }

    /**
     * 自动增加php auth信息
     */
    private function autoAddAuth($data)
    {
        // 获取不包含当前表的干净的路由
        $cleanRoute = $this->getNoCurrentAuthConfig($data);
        $temp = $this->generatePhpAuth($data);

        $authResult = $cleanRoute . PHP_EOL . $temp;
        $authPath = app()->getAppPath() . 'config' . DIRECTORY_SEPARATOR . 'Authority.yml';

        $this->writeFile($authPath, $authResult, true);
    }

    /**
     * 生成vue列表页
     */
    private function generateVueIndex($data)
    {
        // 表名无前缀
        $database = config('database.connections')[config('database.default')];
        $table = str_replace($database['prefix'], '', $data['table']);
        return View::fetch('code_generation/vue/index', ['table' => $table, 'columns' => $data['tableColumns']]);
    }

    /**
     * 生成vue编辑页
     */
    private function generateVueEdit($data)
    {
        // 表名无前缀
        $database = config('database.connections')[config('database.default')];
        $table = str_replace($database['prefix'], '', $data['table']);
        return View::fetch('code_generation/vue/components/Edit', ['table' => $table, 'model' => $data['model'], 'columns' => $data['editColumns']]);
    }

    /**
     * 生成vue编辑页
     */
    private function generateVueDetail($data)
    {
        // 表名无前缀
        $database = config('database.connections')[config('database.default')];
        $table = str_replace($database['prefix'], '', $data['table']);
        return View::fetch('code_generation/vue/components/Detail', ['table' => $table, 'columns' => $data['editColumns']]);
    }

    /**
     * 生成vue api的接口文件
     */
    private function generateVueApi($data)
    {
        $fetchData = ['model' => $data['model']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'vue' . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * 生成vue router的接口文件
     */
    private function generateVueRoute($data)
    {
        $fetchData = ['model' => $data['model'], 'comment' => $data['comment']];
        $tempPath = app()->getAppPath() . 'view' . DIRECTORY_SEPARATOR . 'code_generation' . DIRECTORY_SEPARATOR . 'vue' . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . 'Temp.qct';
        $temp = file_get_contents($tempPath);

        $temp = $this->phpCodeFetch($temp, $fetchData);
        return $temp;
    }

    /**
     * @param string $file 文件绝对地址
     * @param string $content 写入文件的内容
     * @param false $isRewrite 是否覆盖已存在的文件
     */
    private function writeFile($file, $content, $isRewrite = false)
    {
        $this->mkdirs(dirname($file));
        if (file_exists($file)) {
            if ($isRewrite) {
                file_put_contents($file, $content);
            }
        } else {
            file_put_contents($file, $content);
        }
    }

    private function mkdirs($path, $mode = 0777)
    {
        if (is_dir($path) || @mkdir($path, $mode)) return true;
        if (!$this->mkdirs(dirname($path), $mode)) return false;
        return @mkdir($path, $mode);

    }

    /**
     * 获取 app下面所有模块的名称
     */
    public function getAllModules()
    {

        $result = [];

        $handler = opendir(app()->getBasePath());
        //2、循环的读取目录下的所有文件
        /* 其中$filename = readdir($handler)是每次循环的时候将读取的文件名赋值给$filename，为了不陷于死循环，所以还要让$filename !== false。一定要用!==，因为如果某个文件名如果叫’0′，或者某些被系统认为是代表false，用!=就会停止循环 */
        while (($filename = readdir($handler)) !== false) {
            // 3、目录下都会有两个文件，名字为’.'和‘..’，不要对他们进行操作
            if ($filename != '.' && $filename != '..') {
                // 4、进行处理
                if (is_dir(app()->getBasePath() . $filename)) {
                    array_push($result, $filename);
                }
            }
        }
        //5、关闭目录
        closedir($handler);

        $denyModule = config('app.deny_app_list');
        $result = array_diff($result, $denyModule);
        sort($result);
        return array_map(function ($item) {
            return [
                'label' => $item,
                'value' => $item
            ];
        }, $result);

    }

    /**
     * 模板功能替换
     * @param string $temp 模板内容
     * @param array $data 模板渲染变量
     */
    private function phpCodeFetch($temp, $data)
    {

        foreach ($data as $k => $v) {
            $temp = str_replace('{$' . $k . '}', $v, $temp);
        }
        return $temp;
    }

    /**
     * @param $data
     * @return array|string|string[]|null
     * 获取不包含当前路由信息的路由配置信息
     */
    private function getNoCurrentTableRoute($data)
    {
        $fmodel = $data['fmodel'];
        $rule = '~// Route-' . $fmodel . '-Start(\s|\S)*// Route-' . $fmodel . '-End~';

        $routePath = app()->getAppPath() . DIRECTORY_SEPARATOR . 'route' . DIRECTORY_SEPARATOR . 'app.php';
        $routeContent = file_get_contents($routePath);

        return preg_replace($rule, '', $routeContent);
    }

    /**
     * @param $data
     * @return array|string|string[]|null
     * 获取不包含当前路由信息的路由配置信息
     */
    private function getNoCurrentAuthConfig($data)
    {
        $model = $data['model'];
        $comment = $data['comment'];
        $rule = '~- code: ' . $model . '(\s|\S)*' . $comment . '详情~';

        $authPath = app()->getAppPath() . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'Authority.yml';
        $authPathContent = file_get_contents($authPath);

        return preg_replace($rule, '', $authPathContent);
    }

    /**
     * 将数据库中的表名变成帕斯卡命名
     */
    private function tableToClassName($table){
        $names = explode('_',$table);
        $names = array_map(function($item){
            return ucwords($item);
        },$names);
        return implode('',$names);
    }

}